by Kenn Scribner
In This Chapter
If youve never developed COM components in plain C++, there are many reasons why using the Active Template Library (ATL) from within an MFC application makes sense. Before you learn about the specifics of COM, however, step back in time a moment and remember how programmers used to develop Windows applications.
Imagine yourself writing a large-scale Windows program, but in 1990. What, no MFC? No, you cannot use MFCthe MFC framework hadnt been written yet. (Or at least MFC hadnt been released to the general development community.) You must write code to handle each and every Windows message you want to process. You crack every WM_COMMAND, read every lParam, and generally write a ton of grungy support code. For your efforts, you have total control over the flow and operation of your application. On the other hand, those same efforts cost you time to market, time to reach code complete, and more bugs (or features) than anyone should have to face.
This is the thrust behind MFC, or at least one of the main goals. You fire up Developer Studio, click a few buttons, and whammo! you have a working Windows application. The application initially lacks many of the features your low-level design would have you incorporate, but you didnt spend a lot of time creating the basic framework to support those same features. You get right to the meat of the application much more quickly, and you can feel quite confident the basic framework code is relatively bug-free.
Well, this is the same argument I would make regarding the history of ATL and COM. Pure COM, written in C++ (or C, if youve really been doing it a long time), is just as detail-oriented and bug-prone as Windows code using the old Software Development Kit. In fact, you could easily find COM programmers who would tell you its more detailed-oriented and bug-prone. And theyd be correctCOM programming can be far more challenging (and thats challenging in the difficult sense). The simple stuff isnt so bad, but the truly brilliant code takes work.
Microsoft took hundreds of man-years of COM development experience and wrapped it up in ATL. Whats even more intriguing is the T in ATL: template Were literally talking C++ templates here, which are recent and exciting additions to the C++ specification. Not only will you save time coding your application, but youll also gain those hundreds of man-years of Microsoft coding experience and be at the forefront of C++ development and code reuse technology.
You will begin your examination of ATL within an MFC application by looking at ways ATL might help you manage a few of the details COM requires you to handle. First, you will examine the COM objects pointer itself and how you might better tackle the objects reference count. Then, you will look more deeply into both COM binary strings, or BSTRs, and converting textual information from Unicode (wide characters) to something more easily handled programmatically. Finally, you will unlock some of the secrets behind the VARIANT and see how ATL can help you there as well. Lets begin with COM pointers.
When you work with COM, and in particular some specific COM object, all you are really doing is accessing the given objects member functions through a pointer. Sure, the plumbing is a bit more extravagant with COM, as your pointer could actually access an object outside of your current address space, or even on a different computer over a network connection. But its still just a pointer, and you call functions using the pointer just as you always have in the past. To you, the objects consumer, this code is equivalent:
pObject1->Foo(); // a C++ object
and:
pObject2->Bar(); // a COM object
Yes, the objects are of different types (a C++ object versus a COM object), but youre accessing the member function in the same mannerthrough a pointer.
But when using COM objects, you must be cognizant of the objects reference count (as you saw in Chapter 10, COM). In C++, you would delete the object. When using COM, you release it by using the objects Release() method (which is guaranteed by the rules of COM to exist). To handle the situations where you dont want to forget to delete the object (or release it), smart pointers were created. A smart pointer is a C++ class that encapsulates the actual memory pointer. The true beauty of a smart pointer is that it can handle the object deletion for you when the smart pointer goes out of scope. At that time its destructor is called, and it may then delete or release the constituent object for you. This relieves you of the burden of doing it yourself. By definition, you cant forget to delete or release the object. The C++ language handles the details for you.
Memory leaks are bad enough. But rogue COM objects can be truly nasty (your COM object becomes rogue when your pointer variable leaves its scope without releasing the object). These COM objects might be dynamic link libraries (DLLs) loaded into your address space, munching your valuable virtual and resource memory, or they might be actual processes, consuming CPU cycles and system resources you should be using. They can even hang the system. Therefore, its usually desirable to use a smart pointer when dealing with COM objects. Why invite disaster?
ATL has the ideal solutiona smart pointer template. Actually, there are two major varieties of ATL smart COM pointer templates, and you select the most appropriate based on the mechanics of obtaining the COM pointer. If you create the object from dust, so to speak, youll use the CComPtr template class:
template< class T > class CComPtr
On the other hand, if youre calling IUnknown::QueryInterface() (see Chapter 10) though a COM object pointer you have already obtained, youll use ATLs CComQIPtr template:
template< class T, const IID* piid > class CComQIPtr